package net.nettytlv;

import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Sharable
public class ConnectChannelHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("channelInactive ...");
        throw new Exception("Channel inactive.");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.info("channelActive ...");

        // 初始化心跳计数器
        Client client = new Client();
        client.getPingTime().incrementAndGet();
        ctx.channel().attr(Client.KEY).set(client);

        ctx.writeAndFlush(PacketUtil.createPingReq(client.getId()), ctx.channel().voidPromise());
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            log.debug("userEventTriggered id={}, idleStateEvent= {}", ctx.channel().id().toString(),
                    idleStateEvent.state());
            switch (idleStateEvent.state()) {
                case WRITER_IDLE:
                    writerIdleHandler(ctx);
                    break;

                default:
                    break;
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();

        // 重连
        TlvClient.getInstance().connect();
    }

    /**
     * 写操作闲置处理.
     *
     * 发送心跳请求维持连接.
     */
    private void writerIdleHandler(ChannelHandlerContext ctx) throws Exception {
        if (!ctx.channel().hasAttr(Client.KEY)) {
            throw new Exception("Channel not found client.");
        }

        Client client = ctx.channel().attr(Client.KEY).get();
        if (!client.isPingTimeout()) {
            throw new Exception("Channel ping timeout.");
        }

        client.getPingTime().incrementAndGet();
        ctx.writeAndFlush(PacketUtil.createPingReq(client.getId()), ctx.channel().voidPromise());
    }
}
